import './App.css'
import Pillar from './components/pillar'
import Stack from './components/stack'
import React, { Component } from 'react'
import { getRandomColor } from './utils/color'
import { v4 } from 'uuid'
import anime from 'animejs/lib/anime.es'

export default class App extends Component {
    constructor() {
        super()
        this.state = {
            annulus_a: [],
            annulus_b: [],
            annulus_c: [],
            height: 100,
            count: 3,
            step: 0,
            g: null,
            steps: [],
            test: [],
        }
    }
    /** 初始化游戏 */
    resetGame() {
        this.setState({
            annulus_a: [],
            annulus_b: [],
            annulus_c: [],
            height: 100,
            step: 0,
            g: null,
            steps: [],
            playing: 0,
        })
        this.initAnnulus()
        this.calculateHeight()
        this.setState({ g: this.hanoi(this.state.count, 'A', 'B', 'C') })
        this.getHanoiStack(this.state.count, 'A', 'B', 'C')
    }
    /** 初始化环 */
    initAnnulus() {
        let annulus = []
        for (let i = this.state.count; i >= 1; i--) {
            annulus.push({
                id: v4(),
                name: Math.abs(this.state.count - i) + 1,
                color: getRandomColor(1),
            })
        }
        this.setState({ annulus_a: annulus })
    }
    /** 计算柱高度 */
    calculateHeight() {
        this.setState({
            height:
                (12 /** 环的底外边距 */ + 20) /** 环高 */ * this.state.count +
                24 /** 柱的外边距 */,
        })
    }
    /**
     * 汉诺塔问题解法
     * @param {number} annulus 环
     * @param {{{name:string,color:string}[]}} pillarOrigin 原柱
     * @param {{{name:string,color:string}[]}} pillarTransition {Promise}过渡柱
     * @param {{{name:string,color:string}[]}} pillarTarget {目标柱Promise}
     * @returns {Promise<void>}
     */
    *hanoi(
        /** 环 */
        annulus = 1,
        /** 原柱 */
        pillarOrigin = [],
        /** 过渡柱 */
        pillarTransition = [],
        /** 目标柱 */
        pillarTarget = []
    ) {
        if (annulus > 1) {
            yield* this.hanoi(
                annulus - 1,
                pillarOrigin,
                pillarTarget,
                pillarTransition
            )
            yield this.setState(
                {
                    guide: `环 ${annulus} from 柱 ${pillarOrigin} to 柱 ${pillarTarget}`,
                },
                this.move(pillarOrigin, pillarTarget)
            )
            yield* this.hanoi(
                annulus - 1,
                pillarTransition,
                pillarOrigin,
                pillarTarget
            )
        } else {
            yield this.setState(
                {
                    guide: `环 1 from 柱 ${pillarOrigin} to 柱 ${pillarTarget}`,
                },
                this.move(pillarOrigin, pillarTarget)
            )
        }
    }
    /**
     * 移动环
     * @param {{name:string,color:string}[]} from
     * @param {{name:string,color:string}[]} to
     */
    move(from, to) {
        // 步数 +1
        this.setState({ step: this.state.step + 1 })

        // 原柱
        let pillar_from = [
            ...this.state['annulus_' + ''.toLowerCase.call(from)],
        ]
        console.log(pillar_from[0])

        // 弹出环
        let theAnnulusWhoWatingForMove = pillar_from.shift()
        this.setState({
            ['annulus_' + ''.toLowerCase.call(from)]: [...pillar_from],
        })
        // 目标柱
        let pillar_to = [...this.state['annulus_' + ''.toLowerCase.call(to)]]
        // 压入环
        pillar_to.unshift(theAnnulusWhoWatingForMove)
        this.setState({
            ['annulus_' + ''.toLowerCase.call(to)]: [...pillar_to],
        })
    }
    /** 自动播放 */
    autoPlay() {
        if (this.state.playing) clearInterval(this.state.playing)
        this.state.g.next()
        this.setState({
            playing: setInterval(() => {
                let done = this.state.g.next().done
                if (done) {
                    clearInterval(this.state.playing)
                    alert('移动完毕！')
                }
            }, 1000),
        })
    }
    /** 获取汉诺塔压栈情况 */
    getHanoiStack(
        /** 环 */
        annulus = 1,
        /** 原柱 */
        pillarOrigin = [],
        /** 过渡柱 */
        pillarTransition = [],
        /** 目标柱 */
        pillarTarget = [],
        /** 栈 */
        stack = 1,
        side = ''
    ) {
        return new Promise((resolve) => {
            if (annulus === 0) resolve()
            else {
                this.getHanoiStack(
                    annulus - 1,
                    pillarOrigin,
                    pillarTarget,
                    pillarTransition,
                    stack + 1,
                    'left',
                    `${stack - 1}-${side}`
                ).then(() => {
                    this.setState(
                        {
                            steps: [
                                ...this.state.steps,
                                {
                                    text: `将 ${annulus} 从 ${pillarOrigin} 移动到 ${pillarTarget}，栈层级 ${stack}`,
                                    done: false,
                                    stack,
                                    side,
                                    color: this.state.annulus_a[
                                        Math.abs(
                                            stack - this.state.annulus_a.length
                                        )
                                    ].color,
                                    pStack: `${stack - 1}-${side}`,
                                },
                            ],
                        },
                        () => {
                            this.getHanoiStack(
                                annulus - 1,
                                pillarTransition,
                                pillarOrigin,
                                pillarTarget,
                                stack + 1,
                                'right',
                                `${stack - 1}-${side}`
                            ).then(resolve)
                        }
                    )
                })
            }
        })
    }

    /** UI 渲染 */
    render() {
        return (
            <div className='App'>
                <h1 className='game-name'>图解汉诺塔问题</h1>
                <p className='game-rule__title'>汉诺塔游戏规则</p>
                <ol
                    style={{
                        margin: '12px auto',
                        width: '750px',
                        textAlign: 'left',
                    }}
                >
                    <li className='rule-list-1'>
                        有三根相邻的柱子，标号为 A， B， C。
                    </li>
                    <li className='rule-list-2'>
                        A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘。
                    </li>
                    <li className='rule-list-3'>
                        现在把所有盘子一个一个移动到柱子B上，并且每次移动同一根柱子上都不能出现大盘子在小盘子上方。
                    </li>
                </ol>

                <div className='graphic-area'>
                    <Stack steps={this.state.steps} />
                    <div
                        className='hanoi-pillar'
                        style={{ height: this.state.height + 'px' }}
                    >
                        <Pillar annulus={this.state.annulus_a} name='A 柱' />
                        <Pillar annulus={this.state.annulus_b} name='B 柱' />
                        <Pillar annulus={this.state.annulus_c} name='C 柱' />
                    </div>
                    <div className='game-panel'>
                        <p className='game-steps'>步数：{this.state.step}</p>
                        <p className='game-guide'>
                            说明：{this.state.guide || '等待移动···'}
                        </p>
                    </div>
                </div>

                <div style={{ margin: '24px 0' }}>
                    <label htmlFor='annulus'>环数：</label>{' '}
                    <input
                        id='annulus'
                        type='text'
                        placeholder='请输入环的个数'
                        style={{ width: '30px', marginRight: '12px' }}
                        value={this.state.count}
                        onChange={(e) => {
                            if (e.target.value > 1 && e.target.value < 8)
                                this.setState({ count: e.target.value })
                        }}
                    />
                    <button
                        style={{ marginRight: '12px' }}
                        onClick={() => this.resetGame()}
                    >
                        重置
                    </button>
                    <button
                        className='game-start'
                        style={{ marginRight: '12px' }}
                        onClick={() => this.state.g.next()}
                    >
                        移动！
                    </button>
                    <button onClick={() => this.autoPlay()}>自动移动</button>
                </div>
            </div>
        )
    }

    /** 挂载后 reset 游戏 */
    componentDidMount() {
        this.resetGame()
    }
}
